﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using PolyVoxCore;

namespace PolyXNAx
{
   public class PolyVoxBatchPart
    {
       private Vector3 pPosition;
       private BoundingBox pBounds;

        private VertexBuffer vertexBuffer;
        private IndexBuffer indexBuffer;

        private Region pValidRegion;
        private SurfaceMeshPositionMaterialNormal pSurface;
        private bool hasContent;

        SurfaceExtractorSimpleVolumeMaterialDensityPair62 _extractor;
        PositionMaterialNormalVector vertices;
        VertexPositionNormalTexture[] VerticesNormal;
        short[] Indices;
        Vector2 materialVector;
        Vector3 converse;

        public PolyVoxBatchPart(Vector3 position, int sideSize)
        {
            pPosition = new Vector3(position.X-375,position.Y-375,position.Z-375);
            pValidRegion = new Region(new Vector3DInt32((int)position.X, (int)position.Y, (int)position.Z), new Vector3DInt32((int)position.X + sideSize, (int)position.Y + sideSize, (int)position.Z + sideSize));
            pBounds = new BoundingBox(position, new Vector3(position.X + sideSize, position.Y + sideSize, position.Z + sideSize));
            pSurface = new SurfaceMeshPositionMaterialNormal();
            materialVector = new Vector2(0, 0);

        }

        public void calculate(SimpleVolumeMaterialDensityPair62 volumeData,GraphicsDevice device)
        {

            _extractor= new SurfaceExtractorSimpleVolumeMaterialDensityPair62(volumeData, pValidRegion, pSurface);
            _extractor.execute();
             vertices = pSurface.getVertices();
            if (vertices.Count <= 0 || device == null)
            {
                hasContent = false;

            }
            else
            {
               VerticesNormal = vertices.Select<PositionMaterialNormal, VertexPositionNormalTexture>(v =>
                {
                   materialVector.X=(v.getMaterial()/64)+0.001f;
                    return new VertexPositionNormalTexture(vector3DFloatIntoConverse(out converse,v.position), vector3DFloatIntoConverse(out converse,v.getNormal()), materialVector);
                 
                }).ToArray();

                // convert indices to xna format
                Indices = pSurface.getIndices().Select(i => (short)i).ToArray();

                vertexBuffer = new VertexBuffer(device,
                     typeof(VertexPositionNormalTexture), VerticesNormal.Length,
                     BufferUsage.None);
                vertexBuffer.SetData<VertexPositionNormalTexture>(VerticesNormal);

                indexBuffer = new IndexBuffer(device,
                    IndexElementSize.SixteenBits, Indices.Length, BufferUsage.None);
                indexBuffer.SetData<short>(Indices);
                hasContent = true;
                return ;
            }
                    
        }

        public void Draw(GraphicsDevice device, BoundingFrustum frustum, BasicEffect effect)
        {
            
            //Part hat zu zeichnendes Material
            if (hasContent)
            {
               
                //Part ist im sichtbaren bereich
              //  if ( frustum.Intersects(this.pBounds))
                {
                    
                    device.Indices = this.indexBuffer;
                    device.SetVertexBuffer(this.vertexBuffer);
                    effect.World = Matrix.CreateTranslation(this.pPosition);
                    foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                    {
                        pass.Apply();
                        device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertexBuffer.VertexCount, 0, indexBuffer.IndexCount / 3);
                    }
                }
            }
        }

        public bool needDraw(BoundingFrustum frustum)
        {
            return (hasContent && frustum.Contains(this.pBounds) != ContainmentType.Disjoint);
        }

        private Vector3 vector3DFloatIntoConverse(out Vector3 result,Vector3DFloat vector3DFloat)
        {
            result.X=vector3DFloat.getX();
            result.Y = vector3DFloat.getY();
            result.Z = vector3DFloat.getZ();
            return result;
            
        }
    }
}
